home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / net / bind-contrib.tar.gz / bind-contrib.tar / contrib / nslint-1.5.1 / nslint.c < prev    next >
C/C++ Source or Header  |  1996-10-25  |  32KB  |  1,426 lines

  1. /*
  2.  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that: (1) source code distributions
  7.  * retain the above copyright notice and this paragraph in its entirety, (2)
  8.  * distributions including binary code include the above copyright notice and
  9.  * this paragraph in its entirety in the documentation or other materials
  10.  * provided with the distribution, and (3) all advertising materials mentioning
  11.  * features or use of this software display the following acknowledgement:
  12.  * ``This product includes software developed by the University of California,
  13.  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
  14.  * the University nor the names of its contributors may be used to endorse
  15.  * or promote products derived from this software without specific prior
  16.  * written permission.
  17.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  18.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  19.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  20.  */
  21. #ifndef lint
  22. char copyright[] =
  23.     "@(#) Copyright (c) 1992, 1993, 1994, 1995, 1996\n\
  24. The Regents of the University of California.  All rights reserved.\n";
  25. static char rcsid[] =
  26.     "@(#) $Header: /proj/src/isc/cvs-1/bind/contrib/nslint-1.5.1/nslint.c,v 1.1 1996/10/25 17:13:36 vixie Exp $ (LBL)";
  27. #endif
  28. /*
  29.  * nslint - perform consistency checks on dns files
  30.  */
  31.  
  32. #include <sys/types.h>
  33.  
  34. #include <netinet/in.h>
  35.  
  36. #include <arpa/inet.h>
  37.  
  38. #include <ctype.h>
  39. #include <errno.h>
  40. #ifdef HAVE_MALLOC_H
  41. #include <malloc.h>
  42. #endif
  43. #include <netdb.h>
  44. #include <stdio.h>
  45. #include <stdlib.h>
  46. #include <string.h>
  47. #include <time.h>
  48. #include <unistd.h>
  49.  
  50. #include "gnuc.h"
  51. #ifdef HAVE_OS_PROTO_H
  52. #include "os-proto.h"
  53. #endif
  54.  
  55. #define NSLINTBOOT "nslint.boot"    /* default nslint.boot file */
  56.  
  57. /* item struct */
  58. struct item {
  59.     char    *host;        /* pointer to hostname */
  60.     u_long    addr;        /* ip address */
  61.     int    records;    /* resource records seen */
  62.     int    flags;        /* flags word */
  63. };
  64.  
  65. /* Resource records seen */
  66. #define REC_A        0x0001
  67. #define REC_PTR        0x0002
  68. #define REC_WKS        0x0004
  69. #define REC_HINFO    0x0008
  70. #define REC_MX        0x0010
  71. #define REC_CNAME    0x0020
  72. #define REC_NS        0x0040
  73. #define REC_SOA        0x0080
  74. #define REC_RP        0x0100
  75. #define REC_TXT        0x0200
  76.  
  77. /* These aren't real records */
  78. #define REC_OTHER    0x0400
  79. #define REC_REF        0x0800
  80. #define REC_UNKNOWN    0x1000
  81.  
  82. /* Test for records we want to map to REC_OTHER */
  83. #define MASK_TEST_REC (REC_WKS | REC_HINFO | \
  84.     REC_MX | REC_NS | REC_SOA | REC_RP | REC_TXT | REC_UNKNOWN)
  85.  
  86. /* Mask away records we don't care about in the final processing to REC_OTHER */
  87. #define MASK_CHECK_REC \
  88.     (REC_A | REC_PTR | REC_CNAME | REC_REF | REC_OTHER)
  89.  
  90. /* Test for records we want to check for duplicate name detection */
  91. #define MASK_TEST_DUP \
  92.     (REC_A | REC_HINFO)
  93.  
  94. /* Flags */
  95. #define FLG_SELFMX    0x001    /* mx record refers to self */
  96. #define FLG_MXREF    0x002    /* this record refered to by a mx record */
  97. #define FLG_SMTPWKS    0x004    /* saw wks with smtp/tcp */
  98. #define FLG_ALLOWDUPA    0x008    /* allow duplicate a records */
  99.  
  100. /* Test for smtp problems */
  101. #define MASK_TEST_SMTP \
  102.     (FLG_SELFMX | FLG_SMTPWKS)
  103.  
  104.  
  105. #define ITEMSIZE (1 << 16)    /* power of two */
  106. #define ITEMHASH(str, h, p) \
  107.     for (p = str, h = 0; *p != '.' && *p != '\0';) h = (h << 5) - h + *p++
  108.  
  109. struct    item items[ITEMSIZE];
  110. int    itemcnt;        /* count of items */
  111.  
  112. /* Hostname string storage */
  113. #define STRSIZE 8192;        /* size to malloc when more space is needed */
  114. char    *strptr;        /* pointer to string pool */
  115. int    strsize;        /* size of space left in pool */
  116.  
  117. int    debug;
  118. int    errors;
  119. char    *bootfile = "/etc/named.boot";
  120. char    *nslintboot;
  121. char    *prog;
  122. char    *cwd = ".";
  123.  
  124. char *tcpserv[512];        /* valid tcp service names */
  125. char *udpserv[512];        /* valid udp service names */
  126.  
  127. /* Forwards */
  128. static    inline void add_domain __P((char *, char *));
  129. void    checkdups __P((struct item *, int));
  130. int    checkserv __P((char *, char **p));
  131. int    checkdots __P((char *));
  132. int    checkwks __P((FILE *, char *, int *, char **));
  133. int    cmpitem __P((const void *, const void *));
  134. int    doit __P((char *, int));
  135. void    initserv __P((void));
  136. char    *intoa __P((u_long));
  137. int    nslint __P((void));
  138. int    main __P((int, char **));
  139. u_long    parseinaddr __P((char *));
  140. u_long    parseptr __P((char *, u_long, char **));
  141. char    *parsequoted __P((char *));
  142. void    process __P((char *, char *));
  143. char    *savestr __P((char *));
  144. void    updateitem __P((char *, u_long, int, int));
  145. __dead    void usage __P((void)) __attribute__((volatile));
  146.  
  147. extern    char *optarg;
  148. extern    int optind, opterr;
  149.  
  150. /* add domain if necessary */
  151. static inline void
  152. add_domain(name, domain)
  153.     register char *name, *domain;
  154. {
  155.     register char *cp;
  156.  
  157.     cp = name + strlen(name) - 1;
  158.     /* Kill trailing white space */
  159.     while (cp > name && isspace(*cp))
  160.         *cp-- = '\0';
  161.     /* If necessary, append domain */
  162.     if (cp > name && *cp++ != '.') {
  163.         if (*domain != '.')
  164.             *cp++ = '.';
  165.         (void)strcpy(cp, domain);
  166.     }
  167.     /* XXX should we insure a trailing dot? */
  168. }
  169.  
  170. int
  171. main(argc, argv)
  172.     int argc;
  173.     char **argv;
  174. {
  175.     register char *cp;
  176.     register int op, status;
  177.  
  178.     if ((cp = strrchr(argv[0], '/')) != NULL)
  179.         prog = cp + 1;
  180.     else
  181.         prog = argv[0];
  182.  
  183.     while ((op = getopt(argc, argv, "b:B:d")) != -1)
  184.         switch (op) {
  185.  
  186.         case 'b':
  187.             bootfile = optarg;
  188.             break;
  189.  
  190.         case 'B':
  191.             nslintboot = optarg;
  192.             break;
  193.  
  194.         case 'd':
  195.             ++debug;
  196.             break;
  197.  
  198.         default:
  199.             usage();
  200.         }
  201.     if (optind != argc)
  202.         usage();
  203.  
  204.     initserv();
  205.     status = doit(bootfile, 1);
  206.     if (nslintboot != NULL)
  207.         status |= doit(nslintboot, 1);
  208.     else
  209.         status |= doit(NSLINTBOOT, 0);
  210.     status |= nslint();
  211.     exit (status);
  212. }
  213.  
  214. /* List of keywords we can safely ignore */
  215. static char *okkeywords[] = {
  216.     "bogusns",
  217.     "cache",
  218.     "domain",
  219.     "forwarders",
  220.     "max-fetch",
  221.     "options",
  222.     "secondary",
  223.     "slave",
  224.     "sortlist",
  225.     "stub",
  226.     "tcplist",
  227.     "xfrnets",
  228.     NULL
  229. };
  230.  
  231. int
  232. doit(file, mustexist)
  233.     char *file;
  234.     int mustexist;
  235. {
  236.     register int n;
  237.     register char *cp, *cp2, **pp;
  238.     register FILE *f;
  239.     char buf[1024], name[128];
  240.  
  241.     errno = 0;
  242.     f = fopen(file, "r");
  243.     if (f == NULL) {
  244.         /* Not an error if it doesn't exist */
  245.         if (!mustexist && errno == ENOENT) {
  246.             if (debug > 1)
  247.                 printf(
  248.                     "%s: doit: %s doesn't exist (ignoring)\n",
  249.                     prog, file);
  250.             return (0);
  251.         }
  252.         fprintf(stderr, "%s: %s: %s\n", prog, file, strerror(errno));
  253.         exit(1);
  254.     }
  255.     if (debug > 1)
  256.         printf("%s: doit: opened %s\n", prog, file);
  257.  
  258.     n = 0;
  259.     while (fgets(buf, sizeof(buf), f) != NULL) {
  260.         ++n;
  261.  
  262.         /* Skip comments */
  263.         if (buf[0] == ';')
  264.             continue;
  265.         cp = strchr(buf, ';');
  266.         if (cp)
  267.             *cp = '\0';
  268.         cp = buf;
  269.  
  270.         /* Eat leading whitespace */
  271.         while (isspace(*cp))
  272.             ++cp;
  273.  
  274.         /* Skip blank lines */
  275.         if (*cp == '\n' || *cp == '\0')
  276.             continue;
  277.  
  278.         /* Get name */
  279.         cp2 = cp;
  280.         while (!isspace(*cp) && *cp != '\0')
  281.             ++cp;
  282.         *cp++ = '\0';
  283.  
  284.         /* Find next keyword */
  285.         while (isspace(*cp))
  286.             ++cp;
  287.         if (strcasecmp(cp2, "directory") == 0) {
  288.             /* Terminate directory */
  289.             cp2 = cp;
  290.             while (!isspace(*cp) && *cp != '\0')
  291.                 ++cp;
  292.             *cp = '\0';
  293.             if (chdir(cp2) < 0) {
  294.                 ++errors;
  295.                 fprintf(stderr, "%s: can't chdir %s: %s\n",
  296.                     prog, cp2, strerror(errno));
  297.                 exit(1);
  298.             }
  299.             cwd = savestr(cp2);
  300.             continue;
  301.         }
  302.         if (strcasecmp(cp2, "primary") == 0) {
  303.             /* Extract domain */
  304.             cp2 = name;
  305.             while (!isspace(*cp) && *cp != '\0')
  306.                 *cp2++ = *cp++;
  307.             /* Insure trailing dot */
  308.             if (cp2 > name && cp2[-1] != '.')
  309.                 *cp2++ = '.';
  310.             *cp2 = '\0';
  311.  
  312.             /* Find file */
  313.             while (isspace(*cp))
  314.                 ++cp;
  315.  
  316.             /* Terminate directory */
  317.             cp2 = cp;
  318.             while (!isspace(*cp) && *cp != '\0')
  319.                 ++cp;
  320.             *cp = '\0';
  321.  
  322.             /* Process it! */
  323.             process(cp2, name);
  324.             continue;
  325.         }
  326.         for (pp = okkeywords; *pp != NULL; ++pp)
  327.             if (strcasecmp(cp2, *pp) == 0)
  328.                 break;
  329.         if (*pp != NULL)
  330.             continue;
  331.         ++errors;
  332.         fprintf(stderr, "%s: %s:%d: unknown record type \"%s\"\n",
  333.             prog, file, n, cp2);
  334.     }
  335.     (void)fclose(f);
  336.  
  337.     return (errors != 0);
  338. }
  339.  
  340. char inaddr[] = ".in-addr.arpa.";
  341.  
  342. void
  343. process(file, domain)
  344.     char *file, *domain;
  345. {
  346.     register FILE *f;
  347.     register char *cp, *cp2, *cp3, *rtype;
  348.     register int n, sawsoa, flags;
  349.     register u_long addr, net;
  350.     int smtp;
  351.     char buf[1024], name[128], lastname[128];
  352.     char *errstr;
  353.     char *dotfmt = "%s: %s/%s:%d \"%s\" target missing trailing dot: %s\n";
  354.  
  355.     f = fopen(file, "r");
  356.     if (f == NULL) {
  357.         fprintf(stderr, "%s: %s/%s: %s\n",
  358.             prog, cwd, file, strerror(errno));
  359.         ++errors;
  360.         return;
  361.     }
  362.     if (debug > 1)
  363.         printf("%s: process: opened %s/%s\n", prog, cwd, file);
  364.  
  365.     /* Are we doing an in-addr.arpa domain? */
  366.     n = 0;
  367.     cp = domain + strlen(domain) - sizeof(inaddr) + 1;
  368.     if (cp >= domain && strcasecmp(cp, inaddr) == 0) {
  369.         net = parseinaddr(domain);
  370.         if (net == 0) {
  371.             ++errors;
  372.             fprintf(stderr,
  373.                 "%s: %s/%s:%d bad in-addr.arpa domain\n",
  374.                 prog, cwd, file, n);
  375.             return;
  376.         }
  377.     } else
  378.         net = 0;
  379.  
  380.     lastname[0] = '\0';
  381.     sawsoa = 0;
  382.     while (fgets(buf, sizeof(buf), f) != NULL) {
  383.         ++n;
  384.         cp = buf;
  385.         while (*cp != '\0') {
  386.             /* Handle quoted strings (but don't report errors) */
  387.             if (*cp == '"') {
  388.                 ++cp;
  389.                 while (*cp != '"' && *cp != '\n' && *cp != '\0')
  390.                     ++cp;
  391.                 continue;
  392.             }
  393.             if (*cp == '\n' || *cp == ';')
  394.                 break;
  395.             ++cp;
  396.         }
  397.         *cp-- = '\0';
  398.  
  399.         /* Nuke trailing white space */
  400.         while (cp >= buf && isspace(*cp))
  401.             *cp-- = '\0';
  402.  
  403.         cp = buf;
  404.         if (*cp == '\0')
  405.             continue;
  406.  
  407.         /* Eat multi-line soa records */
  408.         if (sawsoa) {
  409.             if (strchr(cp, ')') != NULL)
  410.                 sawsoa = 0;
  411.             continue;
  412.         }
  413.         if (debug > 3)
  414.             printf(">%s<\n", cp);
  415.  
  416.         /* Look for name */
  417.         if (isspace(*cp)) {
  418.             /* Same name as last record */
  419.             if (lastname[0] == '\0') {
  420.                 ++errors;
  421.                 fprintf(stderr,
  422.                     "%s: %s/%s:%d no default name\n",
  423.                     prog, cwd, file, n);
  424.                 continue;
  425.             }
  426.             (void)strcpy(name, lastname);
  427.         } else {
  428.             /* Extract name */
  429.             cp2 = name;
  430.             while (!isspace(*cp) && *cp != '\0')
  431.                 *cp2++ = *cp++;
  432.             *cp2 = '\0';
  433.  
  434.             /* Check for domain shorthand */
  435.             if (name[0] == '@' && name[1] == '\0')
  436.                 (void)strcpy(name, domain);
  437.             (void)strcpy(lastname, name);
  438.         }
  439.  
  440.         /* Find next token */
  441.         while (isspace(*cp))
  442.             ++cp;
  443.  
  444.         /* Handle includes (gag) */
  445.         if (name[0] == '$' && strcasecmp(name, "$include") == 0) {
  446.             /* Extract filename */
  447.             cp2 = name;
  448.             while (!isspace(*cp) && *cp != '\0')
  449.                 *cp2++ = *cp++;
  450.             *cp2 = '\0';
  451.  
  452.             /* Look for optional domain */
  453.             while (isspace(*cp))
  454.                 ++cp;
  455.             if (*cp == '\0')
  456.                 process(name, domain);
  457.             else {
  458.                 cp2 = cp;
  459.                 /* Terminate optional domain */
  460.                 while (!isspace(*cp) && *cp != '\0')
  461.                     ++cp;
  462.                 *cp = '\0';
  463.                 process(name, cp2);
  464.             }
  465.             continue;
  466.         }
  467.  
  468.         /* Handle $origin */
  469.         if (name[0] == '$' && strcasecmp(name, "$origin") == 0) {
  470.             /* Extract domain */
  471.             cp2 = domain;
  472.             while (!isspace(*cp) && *cp != '\0')
  473.                 *cp2++ = *cp++;
  474.             *cp2 = '\0';
  475.             lastname[0] = '\0';
  476.  
  477.             /* Are we doing an in-addr.arpa domain? */
  478.             cp = domain + strlen(domain) - sizeof(inaddr) + 1;
  479.             if (cp >= domain && strcasecmp(cp, inaddr) == 0)
  480.                 net = parseinaddr(domain);
  481.             else
  482.                 net = 0;
  483.             continue;
  484.         }
  485.  
  486.         /* Eat ttl if present */
  487.         if (isdigit(*cp)) {
  488.             do {
  489.                 ++cp;
  490.             } while (isdigit(*cp));
  491.  
  492.             if (!isspace(*cp)) {
  493.                 ++errors;
  494.                 fprintf(stderr, "%s: %s/%s:%d bad ttl\n",
  495.                     prog, cwd, file, n);
  496.                 continue;
  497.             }
  498.  
  499.             /* Find next token */
  500.             ++cp;
  501.             while (isspace(*cp))
  502.                 ++cp;
  503.         }
  504.  
  505.         /* Eat optional "in" */
  506.         if ((cp[0] == 'i' || cp[0] == 'I') &&
  507.             (cp[1] == 'n' || cp[1] == 'N') && isspace(cp[2])) {
  508.             /* Find next token */
  509.             cp += 3;
  510.             while (isspace(*cp))
  511.                 ++cp;
  512.         }
  513.  
  514.         /* Find end of record type, converting to lower case */
  515.         rtype = cp;
  516.         for (rtype = cp; !isspace(*cp) && *cp != '\0'; ++cp)
  517.             if (isupper(*cp))
  518.                 *cp = tolower(*cp);
  519.         *cp++ = '\0';
  520.  
  521.         /* Find "the rest" */
  522.         while (isspace(*cp))
  523.             ++cp;
  524.  
  525.         /* Check for names with dots but no trailing dot */
  526.         if (!isdigit(*name) && checkdots(name)) {
  527.             ++errors;
  528.             fprintf(stderr,
  529.               "%s: %s/%s:%d \"%s\" name missing trailing dot: %s\n",
  530.                 prog, cwd, file, n, rtype, name);
  531.         }
  532.  
  533. #define CHECK3(p, a, b, c) \
  534.     (p[0] == (a) && p[1] == (b) && p[2] == (c) && p[3] == '\0')
  535. #define CHECK2(p, a, b) \
  536.     (p[0] == (a) && p[1] == (b) && p[2] == '\0')
  537. #define CHECKDOT(p) \
  538.     (p[0] == '.' && p[1] == '\0')
  539.  
  540.         if (rtype[0] == 'a' && rtype[1] == '\0') {
  541.             /* Handle "a" record */
  542.             add_domain(name, domain);
  543.             addr = htonl(inet_addr(cp));
  544.             if ((int)addr == -1) {
  545.                 ++errors;
  546.                 cp2 = cp + strlen(cp) - 1;
  547.                 if (cp2 >= cp && *cp2 == '\n')
  548.                     *cp2 = '\0';
  549.                 fprintf(stderr,
  550.                 "%s: %s/%s:%d bad \"a\" record ip addr \"%s\"\n",
  551.                     prog, cwd, file, n, cp);
  552.                 continue;
  553.             }
  554.             updateitem(name, addr, REC_A, 0);
  555.         } else if (CHECK3(rtype, 'p', 't', 'r')) {
  556.             /* Handle "ptr" record */
  557.             if (checkdots(cp)) {
  558.                 ++errors;
  559.                 fprintf(stderr, dotfmt,
  560.                     prog, cwd, file, n, rtype, cp);
  561.             }
  562.             add_domain(cp, domain);
  563.             errstr = NULL;
  564.             addr = parseptr(name, net, &errstr);
  565.             if (errstr != NULL) {
  566.                 ++errors;
  567.                 fprintf(stderr,
  568.             "%s: %s/%s:%d bad \"ptr\" record (%s) ip addr \"%s\"\n",
  569.                     prog, cwd, file, n, errstr, name);
  570.                 continue;
  571.             }
  572.             updateitem(cp, addr, REC_PTR, 0);
  573.         } else if (CHECK3(rtype, 's', 'o', 'a')) {
  574.             /* Handle "soa" record */
  575.             if (!CHECKDOT(name)) {
  576.                 add_domain(name, domain);
  577.                 updateitem(name, 0, REC_SOA, 0);
  578.             }
  579.             /* Don't eat lines unless a multi-line SOA */
  580.             if (strchr(cp, ')') == NULL)
  581.                 ++sawsoa;
  582.         } else if (CHECK3(rtype, 'w', 'k', 's')) {
  583.             /* Handle "wks" record */
  584.             addr = htonl(inet_addr(cp));
  585.             if ((int)addr == -1) {
  586.                 ++errors;
  587.                 cp2 = cp;
  588.                 while (!isspace(*cp2) && *cp2 != '\0')
  589.                     ++cp2;
  590.                 *cp2 = '\0';
  591.                 fprintf(stderr,
  592.                 "%s: %s/%s:%d bad \"wks\" record ip addr \"%s\"\n",
  593.                     prog, cwd, file, n, cp);
  594.                 continue;
  595.             }
  596.             /* Step over ip address */
  597.             while (*cp == '.' || isdigit(*cp))
  598.                 ++cp;
  599.             while (isspace(*cp))
  600.                 *cp++ = '\0';
  601.             /* Make sure services are legit */
  602.             errstr = NULL;
  603.             n += checkwks(f, cp, &smtp, &errstr);
  604.             if (errstr != NULL) {
  605.                 ++errors;
  606.                 fprintf(stderr,
  607.                     "%s: %s/%s:%d bad \"wks\" record (%s)\n",
  608.                     prog, cwd, file, n, errstr);
  609.                 continue;
  610.             }
  611.             add_domain(name, domain);
  612.             updateitem(name, addr, REC_WKS, smtp ? FLG_SMTPWKS : 0);
  613.             /* XXX check to see if ip address records exists? */
  614.         } else if (rtype[0] == 'h' && strcmp(rtype, "hinfo") == 0) {
  615.             /* Handle "hinfo" record */
  616.             add_domain(name, domain);
  617.             updateitem(name, 0, REC_HINFO, 0);
  618.             cp2 = cp;
  619.             cp = parsequoted(cp);
  620.             if (cp == NULL) {
  621.                 ++errors;
  622.                 fprintf(stderr,
  623.                 "%s: %s/%s:%d \"hinfo\" missing quote: %s\n",
  624.                     prog, cwd, file, n, cp2);
  625.                 continue;
  626.             }
  627.             if (!isspace(*cp)) {
  628.                 ++errors;
  629.                 fprintf(stderr,
  630.                 "%s: %s/%s:%d \"hinfo\" missing white space: %s\n",
  631.                     prog, cwd, file, n, cp2);
  632.                 continue;
  633.             }
  634.             ++cp;
  635.             while (isspace(*cp))
  636.                 ++cp;
  637.             if (*cp == '\0') {
  638.                 ++errors;
  639.                 fprintf(stderr,
  640.                 "%s: %s/%s:%d \"hinfo\" missing keyword: %s\n",
  641.                     prog, cwd, file, n, cp2);
  642.                 continue;
  643.             }
  644.             cp = parsequoted(cp);
  645.             if (cp == NULL) {
  646.                 ++errors;
  647.                 fprintf(stderr,
  648.                 "%s: %s/%s:%d \"hinfo\" missing quote: %s\n",
  649.                     prog, cwd, file, n, cp2);
  650.                 continue;
  651.             }
  652.             if (*cp != '\0') {
  653.                 ++errors;
  654.                 fprintf(stderr,
  655.             "%s: %s/%s:%d \"hinfo\" garbage after keywords: %s\n",
  656.                     prog, cwd, file, n, cp2);
  657.                 continue;
  658.             }
  659.         } else if (CHECK2(rtype, 'm', 'x')) {
  660.             /* Handle "mx" record */
  661.             add_domain(name, domain);
  662.             updateitem(name, 0, REC_MX, 0);
  663.  
  664.             /* Look for priority */
  665.             if (!isdigit(*cp)) {
  666.                 ++errors;
  667.                 fprintf(stderr,
  668.                     "%s: %s/%s:%d bad \"mx\" priority: %s\n",
  669.                     prog, cwd, file, n, cp);
  670.             }
  671.  
  672.             /* Skip over priority */
  673.             ++cp;
  674.             while (isdigit(*cp))
  675.                 ++cp;
  676.             while (isspace(*cp))
  677.                 ++cp;
  678.             if (*cp == '\0') {
  679.                 ++errors;
  680.                 fprintf(stderr,
  681.                     "%s: %s/%s:%d missing \"mx\" hostname\n",
  682.                     prog, cwd, file, n);
  683.             }
  684.             if (checkdots(cp)) {
  685.                 ++errors;
  686.                 fprintf(stderr, dotfmt,
  687.                     prog, cwd, file, n, rtype, cp);
  688.             }
  689.  
  690.             /* Check to see if mx host exists */
  691.             add_domain(cp, domain);
  692.             flags = FLG_MXREF;
  693.             if (*name == *cp && strcmp(name, cp) == 0)
  694.                 flags |= FLG_SELFMX;
  695.             updateitem(cp, 0, REC_REF, flags);
  696.         } else if (rtype[0] == 'c' && strcmp(rtype, "cname") == 0) {
  697.             /* Handle "cname" record */
  698.             add_domain(name, domain);
  699.             updateitem(name, 0, REC_CNAME, 0);
  700.             if (checkdots(cp)) {
  701.                 ++errors;
  702.                 fprintf(stderr, dotfmt,
  703.                     prog, cwd, file, n, rtype, cp);
  704.             }
  705.  
  706.             /* Make sure cname points somewhere */
  707.             add_domain(cp, domain);
  708.             updateitem(cp, 0, REC_REF, 0);
  709.         } else if (CHECK3(rtype, 't', 'x', 't')) {
  710.             /* Handle "txt" record */
  711.             add_domain(name, domain);
  712.             updateitem(name, 0, REC_TXT, 0);
  713.             cp2 = cp;
  714.             cp = parsequoted(cp);
  715.             if (cp == NULL) {
  716.                 ++errors;
  717.                 fprintf(stderr,
  718.                     "%s: %s/%s:%d \"txt\" missing quote: %s\n",
  719.                     prog, cwd, file, n, cp2);
  720.                 continue;
  721.             }
  722.             while (isspace(*cp))
  723.                 ++cp;
  724.             if (*cp != '\0') {
  725.                 ++errors;
  726.                 fprintf(stderr,
  727.                 "%s: %s/%s:%d \"txt\" garbage after text: %s\n",
  728.                      prog, cwd, file, n, cp2);
  729.                 continue;
  730.             }
  731.         } else if (CHECK2(rtype, 'n', 's')) {
  732.             /* Handle "ns" record */
  733.             if (checkdots(cp)) {
  734.                 ++errors;
  735.                 fprintf(stderr, dotfmt,
  736.                     prog, cwd, file, n, rtype, cp);
  737.             }
  738.             add_domain(cp, domain);
  739.             updateitem(cp, 0, REC_NS, 0);
  740.         } else if (CHECK2(rtype, 'r', 'p')) {
  741.             /* Handle "rp" record */
  742.             add_domain(name, domain);
  743.             updateitem(name, 0, REC_RP, 0);
  744.             cp2 = cp;
  745.  
  746.             /* Step over mailbox name */
  747.             /* XXX could add_domain() and check further */
  748.             while (!isspace(*cp) && *cp != '\0')
  749.                 ++cp;
  750.             if (*cp == '\0') {
  751.                 ++errors;
  752.                 fprintf(stderr,
  753.                 "%s: %s/%s:%d \"rp\" missing text name: %s\n",
  754.                     prog, cwd, file, n, cp2);
  755.                 continue;
  756.             }
  757.             ++cp;
  758.             cp3 = cp;
  759.  
  760.             /* Step over text name */
  761.             while (!isspace(*cp) && *cp != '\0')
  762.                 ++cp;
  763.  
  764.             if (*cp != '\0') {
  765.                 ++errors;
  766.                 fprintf(stderr,
  767.                 "%s: %s/%s:%d \"rp\" garbage after text name: %s\n",
  768.                      prog, cwd, file, n, cp2);
  769.                 continue;
  770.             }
  771.  
  772.             /* Make sure text name points somewhere (if not ".") */
  773.             if (!CHECKDOT(cp3)) {
  774.                 add_domain(cp3, domain);
  775.                 updateitem(cp3, 0, REC_REF, 0);
  776.             }
  777.         } else if (rtype[0] == 'a' && strcmp(rtype, "allowdupa") == 0) {
  778.             /* Handle "allow duplicate a" record */
  779.             add_domain(name, domain);
  780.             addr = htonl(inet_addr(cp));
  781.             if ((int)addr == -1) {
  782.                 ++errors;
  783.                 cp2 = cp + strlen(cp) - 1;
  784.                 if (cp2 >= cp && *cp2 == '\n')
  785.                     *cp2 = '\0';
  786.                 fprintf(stderr,
  787.             "%s: %s/%s:%d bad \"allowdupaa\" record ip addr \"%s\"\n",
  788.                     prog, cwd, file, n, cp);
  789.                 continue;
  790.             }
  791.             updateitem(name, addr, 0, FLG_ALLOWDUPA);
  792.         } else {
  793.             /* Unknown record type */
  794.             ++errors;
  795.             fprintf(stderr,
  796.                 "%s: %s/%s:%d unknown record type \"%s\"\n",
  797.                 prog, cwd, file, n, rtype);
  798.             add_domain(name, domain);
  799.             updateitem(name, 0, REC_UNKNOWN, 0);
  800.         }
  801.     }
  802.     (void)fclose(f);
  803.     return;
  804. }
  805.  
  806. char *
  807. savestr(str)
  808.     register char *str;
  809. {
  810.     register int i;
  811.  
  812.     i = strlen(str) + 1;
  813.     if (i > strsize) {
  814.         strsize = STRSIZE;
  815.         strptr = malloc(strsize);
  816.         if (strptr == NULL) {
  817.             fprintf(stderr, "%s: savestr: malloc: %s\n",
  818.                 prog, strerror(errno));
  819.             exit(1);
  820.         }
  821.     }
  822.     (void)strcpy(strptr, str);
  823.     str = strptr;
  824.     strptr += i;
  825.     strsize -= i;
  826.     return (str);
  827. }
  828.  
  829. /* Records we use to detect duplicates */
  830. static struct duprec {
  831.     int record;
  832.     char *name;
  833. } duprec[] = {
  834.     { REC_A, "a" },
  835.     { REC_HINFO, "hinfo" },
  836.     { 0, NULL },
  837. };
  838.  
  839. void
  840. checkdups(ip, records)
  841.     register struct item *ip;
  842.     register int records;
  843. {
  844.     register struct duprec *dp;
  845.  
  846.     records &= (ip->records & MASK_TEST_DUP);
  847.     if (records == 0)
  848.         return;
  849.     for (dp = duprec; dp->name != NULL; ++dp)
  850.         if ((records & dp->record) != 0) {
  851.             ++errors;
  852.             fprintf(stderr, "%s: multiple \"%s\" records for %s\n",
  853.                 prog, dp->name, ip->host);
  854.             records &= ~dp->record;
  855.         }
  856.     if (records != 0)
  857.         fprintf(stderr, "%s: checkdups: records not zero (%d)\n",
  858.             prog, records);
  859. }
  860.  
  861. void
  862. updateitem(host, addr, records, flags)
  863.     register char *host;
  864.     register u_long addr;
  865.     register int records, flags;
  866. {
  867.     register char *p;
  868.     register int n;
  869.     register u_int i;
  870.     register struct item *ip;
  871.     int foundsome;
  872.  
  873.     n = 0;
  874.     foundsome = 0;
  875.     ITEMHASH(host, i, p);
  876.     ip = &items[i & (ITEMSIZE - 1)];
  877.     while (n < ITEMSIZE && ip->host) {
  878.         if ((addr == 0 || addr == ip->addr || ip->addr == 0) &&
  879.             *host == *ip->host && strcmp(host, ip->host) == 0) {
  880.             ++foundsome;
  881.             if (ip->addr == 0)
  882.                 ip->addr = addr;
  883.             if ((records & MASK_TEST_DUP) != 0)
  884.                 checkdups(ip, records);
  885.             ip->records |= records;
  886.             ip->flags |= flags;
  887.             /* Not done if we wildcard matched the name */
  888.             if (addr)
  889.                 return;
  890.         }
  891.         ++n;
  892.         ++ip;
  893.         if (ip >= &items[ITEMSIZE])
  894.             ip = items;
  895.     }
  896.  
  897.     if (n >= ITEMSIZE) {
  898.         fprintf(stderr, "%s: out of item slots (max %d)\n",
  899.             prog, ITEMSIZE);
  900.         exit(1);
  901.     }
  902.  
  903.     /* Done if we were wildcarding the name (and found entries for it) */
  904.     if (addr == 0 && foundsome)
  905.         return;
  906.  
  907.     /* Didn't find it, make new entry */
  908.     ++itemcnt;
  909.     if (ip->host) {
  910.         fprintf(stderr, "%s: reusing bucket!\n", prog);
  911.         exit(1);
  912.     }
  913.     ip->addr = addr;
  914.     ip->host = savestr(host);
  915.     if ((records & MASK_TEST_DUP) != 0)
  916.         checkdups(ip, records);
  917.     ip->records |= records;
  918.     ip->flags |= flags;
  919. }
  920.  
  921. int
  922. nslint()
  923. {
  924.     register int n, i, records, flags;
  925.     register char *cp;
  926.     register struct item *ip, **ipp, **itemlist;
  927.     register u_long addr, last;
  928.  
  929.     itemlist = (struct item **)calloc(itemcnt, sizeof(*ipp));
  930.     if (itemlist == NULL) {
  931.         fprintf(stderr, "%s: nslint: calloc: %s\n",
  932.             prog, strerror(errno));
  933.         exit(1);
  934.     }
  935.     ipp = itemlist;
  936.     for (n = 0, ip = items; n < ITEMSIZE; ++n, ++ip) {
  937.         if (ip->host == NULL)
  938.             continue;
  939.  
  940.         /* Save entries with addresses for later check */
  941.         if (ip->addr != 0)
  942.             *ipp++ = ip;
  943.  
  944.         if (debug > 1) {
  945.             if (debug > 2)
  946.                 printf("%d\t", n);
  947.             printf("%s\t%s\t0x%x\t0x%x\n",
  948.                 ip->host, intoa(ip->addr), ip->records, ip->flags);
  949.         }
  950.  
  951.         /* Check for illegal hostnames (rfc1034) */
  952.         cp = ip->host;
  953.         if (!isalpha(*cp) && !isdigit(*cp)) {
  954.             ++errors;
  955.             fprintf(stderr,
  956.         "%s: illegal hostname \"%s\" (starts with non-alpha/numeric)\n",
  957.                 prog, ip->host);
  958.         }
  959.         i = 0;
  960.         for (++cp; *cp != '.' && *cp != '\0'; ++cp)
  961.             if (!isalpha(*cp) && !isdigit(*cp) && *cp != '-') {
  962.                 ++errors;
  963.                 fprintf(stderr,
  964.             "%s: illegal hostname \"%s\" ('%c' illegal character)\n",
  965.                     prog, ip->host, *cp);
  966.                 break;
  967.             }
  968.         if (--cp >= ip->host && *cp == '-') {
  969.             ++errors;
  970.             fprintf(stderr,
  971.                 "%s: illegal hostname \"%s\" (ends with '-')\n",
  972.                 prog, ip->host);
  973.         }
  974.  
  975.         /* Check for missing ptr records (ok if also an ns record) */
  976.         records = ip->records & MASK_CHECK_REC;
  977.         if ((ip->records & MASK_TEST_REC) != 0)
  978.             records |= REC_OTHER;
  979.         switch (records) {
  980.  
  981.         case REC_A | REC_OTHER | REC_PTR | REC_REF:
  982.         case REC_A | REC_OTHER | REC_PTR:
  983.         case REC_A | REC_PTR | REC_REF:
  984.         case REC_A | REC_PTR:
  985.         case REC_CNAME | REC_REF:
  986.         case REC_CNAME:
  987.             /* These are O.K. */
  988.             break;
  989.  
  990.         case REC_OTHER | REC_REF:
  991.         case REC_OTHER:
  992.             /*
  993.              * This is only an error if there is an address
  994.              * associated with the hostname; this means
  995.              * there was a wks entry with bogus address.
  996.              * Otherwise, we have an mx or hinfo.
  997.              */
  998.             if (ip->addr != 0) {
  999.                 ++errors;
  1000.                 fprintf(stderr,
  1001.                 "%s: \"wks\" without \"a\" and \"ptr\": %s -> %s\n",
  1002.                     prog, ip->host, intoa(ip->addr));
  1003.             }
  1004.             break;
  1005.  
  1006.         case REC_REF:
  1007.             ++errors;
  1008.             fprintf(stderr,
  1009.                 "%s: name referenced without other records: %s\n",
  1010.                 prog, ip->host);
  1011.             break;
  1012.  
  1013.         case REC_A | REC_OTHER | REC_REF:
  1014.         case REC_A | REC_OTHER:
  1015.         case REC_A | REC_REF:
  1016.         case REC_A:
  1017.             ++errors;
  1018.             fprintf(stderr, "%s: missing \"ptr\": %s -> %s\n",
  1019.                 prog, ip->host, intoa(ip->addr));
  1020.             break;
  1021.  
  1022.         case REC_OTHER | REC_PTR | REC_REF:
  1023.         case REC_OTHER | REC_PTR:
  1024.         case REC_PTR | REC_REF:
  1025.         case REC_PTR:
  1026.             ++errors;
  1027.             fprintf(stderr, "%s: missing \"a\": %s -> %s\n",
  1028.                 prog, ip->host, intoa(ip->addr));
  1029.             break;
  1030.  
  1031.         case REC_A | REC_CNAME | REC_OTHER | REC_PTR | REC_REF:
  1032.         case REC_A | REC_CNAME | REC_OTHER | REC_PTR:
  1033.         case REC_A | REC_CNAME | REC_OTHER | REC_REF:
  1034.         case REC_A | REC_CNAME | REC_OTHER:
  1035.         case REC_A | REC_CNAME | REC_PTR | REC_REF:
  1036.         case REC_A | REC_CNAME | REC_PTR:
  1037.         case REC_A | REC_CNAME | REC_REF:
  1038.         case REC_A | REC_CNAME:
  1039.         case REC_CNAME | REC_OTHER | REC_PTR | REC_REF:
  1040.         case REC_CNAME | REC_OTHER | REC_PTR:
  1041.         case REC_CNAME | REC_OTHER | REC_REF:
  1042.         case REC_CNAME | REC_OTHER:
  1043.         case REC_CNAME | REC_PTR | REC_REF:
  1044.         case REC_CNAME | REC_PTR:
  1045.             ++errors;
  1046.             fprintf(stderr, "%s: \"cname\" %s has other records\n",
  1047.                 prog, ip->host);
  1048.             break;
  1049.  
  1050.         case 0:
  1051.             /* Second level test */
  1052.             if ((ip->records & ~(REC_NS | REC_TXT)) == 0)
  1053.                 break;
  1054.             /* Fall through... */
  1055.  
  1056.         default:
  1057.             ++errors;
  1058.             fprintf(stderr,
  1059.                 "%s: records == 0x%x: can't happen (%s 0x%x)\n",
  1060.                 prog, records, ip->host, ip->records);
  1061.             break;
  1062.         }
  1063.  
  1064.         /* Check for smtp problems */
  1065.         flags = ip->flags & MASK_TEST_SMTP;
  1066.         switch (flags) {
  1067.  
  1068.         case 0:
  1069.         case FLG_SELFMX | FLG_SMTPWKS:
  1070.             /* These are O.K. */
  1071.             break;
  1072.  
  1073.         case FLG_SELFMX:
  1074.             if ((ip->records & REC_WKS) != 0) {
  1075.                 ++errors;
  1076.                 fprintf(stderr,
  1077.                     "%s: smtp/tcp missing from \"wks\": %s\n",
  1078.                     prog, ip->host);
  1079.             }
  1080.             break;
  1081.  
  1082.         case FLG_SMTPWKS:
  1083.             ++errors;
  1084.             fprintf(stderr,
  1085.                 "%s: saw smtp/tcp without self \"mx\": %s\n",
  1086.                 prog, ip->host);
  1087.             break;
  1088.  
  1089.         default:
  1090.             ++errors;
  1091.             fprintf(stderr,
  1092.                 "%s: flags == 0x%x: can't happen (%s)\n",
  1093.                 prog, flags, ip->host);
  1094.         }
  1095.  
  1096.         /* Check for chained MX records */
  1097.         if ((ip->flags & (FLG_SELFMX | FLG_MXREF)) == FLG_MXREF &&
  1098.             (ip->records & REC_MX) != 0) {
  1099.             ++errors;
  1100.             fprintf(stderr,
  1101.                 "%s: \"mx\" referenced by other \"mx\" record: %s\n",
  1102.                 prog, ip->host);
  1103.         }
  1104.     }
  1105.  
  1106.     /* Check for doubly booked addresses */
  1107.     n = ipp - itemlist;
  1108.     qsort(itemlist, n, sizeof(itemlist[0]), cmpitem);
  1109.     last = 0;
  1110.     ip = NULL;
  1111.     for (ipp = itemlist; n > 0; ++ipp, --n) {
  1112.         addr = (*ipp)->addr;
  1113.         if (last == addr &&
  1114.             ((*ipp)->flags & FLG_ALLOWDUPA) == 0 &&
  1115.             (ip->flags & FLG_ALLOWDUPA) == 0) {
  1116.             ++errors;
  1117.             fprintf(stderr, "%s: %s in use by %s and %s\n",
  1118.                 prog, intoa(addr), (*ipp)->host, ip->host);
  1119.         }
  1120.         last = addr;
  1121.         ip = *ipp;
  1122.     }
  1123.  
  1124.     if (debug)
  1125.         printf("%s: %d/%d items used, %d error%s\n", prog, itemcnt,
  1126.             ITEMSIZE, errors, errors == 1 ? "" : "s");
  1127.     return (errors != 0);
  1128. }
  1129.  
  1130. /* Similar to inet_ntoa() */
  1131. char *
  1132. intoa(addr)
  1133.     u_long addr;
  1134. {
  1135.     register char *cp;
  1136.     register u_int byte;
  1137.     register int n;
  1138.     static char buf[sizeof(".xxx.xxx.xxx.xxx")];
  1139.  
  1140.     cp = &buf[sizeof buf];
  1141.     *--cp = '\0';
  1142.  
  1143.     n = 4;
  1144.     do {
  1145.         byte = addr & 0xff;
  1146.         *--cp = byte % 10 + '0';
  1147.         byte /= 10;
  1148.         if (byte > 0) {
  1149.             *--cp = byte % 10 + '0';
  1150.             byte /= 10;
  1151.             if (byte > 0)
  1152.                 *--cp = byte + '0';
  1153.         }
  1154.         *--cp = '.';
  1155.         addr >>= 8;
  1156.     } while (--n > 0);
  1157.  
  1158.     return cp + 1;
  1159. }
  1160.  
  1161. u_long
  1162. parseinaddr(cp)
  1163.     register char *cp;
  1164. {
  1165.     register u_long o, net;
  1166.  
  1167.  
  1168.     net = 0;
  1169.     if (isdigit(*cp)) {
  1170.         o = 0;
  1171.         do {
  1172.             o = o * 10 + (*cp++ - '0');
  1173.         } while (isdigit(*cp));
  1174.         net = o << 24;
  1175.         if (*cp == '.' && isdigit(cp[1])) {
  1176.             ++cp;
  1177.             o = 0;
  1178.             do {
  1179.                 o = o * 10 + (*cp++ - '0');
  1180.             } while (isdigit(*cp));
  1181.             net = (net >> 8) | (o << 24);
  1182.             if (*cp == '.' && isdigit(cp[1])) {
  1183.                 ++cp;
  1184.                 o = 0;
  1185.                 do {
  1186.                     o = o * 10 + (*cp++ - '0');
  1187.                 } while (isdigit(*cp));
  1188.                 net = (net >> 8) | (o << 24);
  1189.             }
  1190.         }
  1191.     }
  1192.     if (strcasecmp(cp, ".in-addr.arpa.") != 0)
  1193.         return (0);
  1194.  
  1195.     return (net);
  1196. }
  1197.  
  1198. u_long
  1199. parseptr(cp, net, errstrp)
  1200.     register char *cp;
  1201.     u_long net;
  1202.     register char **errstrp;
  1203. {
  1204.     register u_long o, addr, addrmask;
  1205.     register int shift;
  1206.  
  1207.     addr = 0;
  1208.     addrmask = 0;
  1209.     shift = 0;
  1210.     while (isdigit(*cp) && shift < 32) {
  1211.         o = 0;
  1212.         do {
  1213.             o = o * 10 + (*cp++ - '0');
  1214.         } while (isdigit(*cp));
  1215.         addr |= o << shift;
  1216.         addrmask |= 0xff << shift;
  1217.         shift += 8;
  1218.         if (*cp != '.')
  1219.             break;
  1220.         ++cp;
  1221.     }
  1222.  
  1223.     if (shift > 32) {
  1224.         *errstrp = "more than 4 octets";
  1225.         return (0);
  1226.     }
  1227.  
  1228.     if (shift == 32 && strcasecmp(cp, "in-addr.arpa.") == 0)
  1229.         return (addr);
  1230.  
  1231.     if (*cp != '\0') {
  1232.         *errstrp = "trailing junk";
  1233.         return (0);
  1234.     }
  1235.     if ((addrmask & net) != 0) {
  1236.         *errstrp = "too many octets for net";
  1237.         return (0);
  1238.     }
  1239.     return (net | addr);
  1240. }
  1241.  
  1242. int
  1243. checkwks(f, proto, smtpp, errstrp)
  1244.     register FILE *f;
  1245.     register char *proto;
  1246.     register int *smtpp;
  1247.     register char **errstrp;
  1248. {
  1249.     register int n, sawparen;
  1250.     register char *cp, *serv, **p;
  1251.     static char errstr[132];
  1252.     char buf[1024];
  1253.  
  1254.     /* Line count */
  1255.     n = 0;
  1256.  
  1257.     /* Terminate protocol */
  1258.     cp = proto;
  1259.     while (!isspace(*cp) && *cp != '\0')
  1260.         ++cp;
  1261.     if (*cp != '\0')
  1262.         *cp++ = '\0';
  1263.     if (strcasecmp(proto, "tcp") == 0)
  1264.         p = tcpserv;
  1265.     else if (strcasecmp(proto, "udp") == 0)
  1266.         p = udpserv;
  1267.     else {
  1268.         (void)sprintf(errstr, "unknown protocol \"%s\"", proto);
  1269.         *errstrp = errstr;
  1270.         return (n);
  1271.     }
  1272.  
  1273.     /* Find services */
  1274.     *smtpp = 0;
  1275.     sawparen = 0;
  1276.     if (*cp == '(') {
  1277.         ++sawparen;
  1278.         ++cp;
  1279.         while (isspace(*cp))
  1280.             ++cp;
  1281.     }
  1282.     for (;;) {
  1283.         if (*cp == '\0') {
  1284.             if (!sawparen)
  1285.                 break;
  1286.             if (fgets(buf, sizeof(buf), f) == NULL) {
  1287.                 *errstrp = "mismatched parens";
  1288.                 return (n);
  1289.             }
  1290.             ++n;
  1291.             cp = buf;
  1292.             while (isspace(*cp))
  1293.                 ++cp;
  1294.         }
  1295.         /* Find end of service, converting to lower case */
  1296.         for (serv = cp; !isspace(*cp) && *cp != '\0'; ++cp)
  1297.             if (isupper(*cp))
  1298.                 *cp = tolower(*cp);
  1299.         if (*cp != '\0')
  1300.             *cp++ = '\0';
  1301.         if (sawparen && *cp == ')') {
  1302.             /* XXX should check for trailing junk */
  1303.             break;
  1304.         }
  1305.         if (*serv == 's' && strcmp(serv, "smtp") == 0)
  1306.             ++*smtpp;
  1307.         if (!checkserv(serv, p)) {
  1308.             sprintf(errstr, "%s/%s unknown", proto, serv);
  1309.             *errstrp = errstr;
  1310.             break;
  1311.         }
  1312.     }
  1313.  
  1314.     return (n);
  1315. }
  1316.  
  1317. int
  1318. checkserv(serv, p)
  1319.     register char *serv, **p;
  1320. {
  1321.     for (; *p != NULL; ++p)
  1322.         if (*serv == **p && strcmp(serv, *p) == 0)
  1323.             return (1);
  1324.     return (0);
  1325. }
  1326.  
  1327. void
  1328. initserv()
  1329. {
  1330.     register char *cp, *cp2, **up, **tp;
  1331.     register struct servent *sp;
  1332.  
  1333.     tp = tcpserv;
  1334.     tp[(sizeof(tcpserv) / sizeof(tcpserv[0])) - 1] = (char *)1;
  1335.     up = udpserv;
  1336.     up[(sizeof(udpserv) / sizeof(udpserv[0])) - 1] = (char *)1;
  1337.     while ((sp = getservent()) != NULL) {
  1338.         cp = savestr(sp->s_name);
  1339.         /* Convert to lower case */
  1340.         for (cp2 = cp; *cp2 != '\0'; ++cp2)
  1341.             if (isupper(*cp2))
  1342.                 *cp2 = tolower(*cp2);
  1343.         if (strcasecmp(sp->s_proto, "tcp") == 0) {
  1344.             *tp++ = cp;
  1345.             if (*tp !=  NULL) {
  1346.                 fprintf(stderr, "%s: too many tcp protocols\n",
  1347.                     prog);
  1348.                 exit(1);
  1349.             }
  1350.             continue;
  1351.         }
  1352.         if (strcasecmp(sp->s_proto, "udp") == 0) {
  1353.             *up++ = cp;
  1354.             if (*up !=  NULL) {
  1355.                 fprintf(stderr, "%s: too many udp protocols\n",
  1356.                     prog);
  1357.                 exit(1);
  1358.             }
  1359.             continue;
  1360.         }
  1361.         fprintf(stderr, "%s: %s/%s unknown service/protocol\n",
  1362.             prog, sp->s_name, sp->s_proto);
  1363.         exit(1);
  1364.     }
  1365. }
  1366.  
  1367. /* Returns true if name contains a dot but not a trailing dot */
  1368. int
  1369. checkdots(name)
  1370.     register char *name;
  1371. {
  1372.     register char *cp;
  1373.  
  1374.     if (strchr(name, '.') == NULL)
  1375.         return (0);
  1376.     cp = name + strlen(name) - 1;
  1377.     if (cp >= name && *cp == '.')
  1378.         return (0);
  1379.     return (1);
  1380. }
  1381.  
  1382. int
  1383. cmpitem(ip1, ip2)
  1384.     const void *ip1, *ip2;
  1385. {
  1386.     register u_long a1, a2;
  1387.  
  1388.     a1 = (*(struct item **)ip1)->addr;
  1389.     a2 = (*(struct item **)ip2)->addr;
  1390.  
  1391.     if (a1 < a2)
  1392.         return (-1);
  1393.     else if (a1 > a2)
  1394.         return (1);
  1395.     else
  1396.         return (0);
  1397. }
  1398.  
  1399. /* Returns a pointer after the next token or quoted string, else NULL */
  1400. char *
  1401. parsequoted(cp)
  1402.     register char *cp;
  1403. {
  1404.  
  1405.     if (*cp == '"') {
  1406.         ++cp;
  1407.         while (*cp != '"' && *cp != '\0')
  1408.             ++cp;
  1409.         if (*cp != '"')
  1410.             return (NULL);
  1411.         ++cp;
  1412.     } else {
  1413.         while (!isspace(*cp) && *cp != '\0')
  1414.             ++cp;
  1415.     }
  1416.     return (cp);
  1417. }
  1418.  
  1419. __dead void
  1420. usage()
  1421. {
  1422.     fprintf(stderr, "usage: %s [-d] [-b named.boot] [-B nslint.boot]\n",
  1423.         prog);
  1424.     exit(1);
  1425. }
  1426.